

#include "stdafx.h"
#include "pf_insert.h"

#include "pf_block.h"
#include "pf_graphic.h"

/**
 * @param parent The graphic this block belongs to.
 */
PF_Insert::PF_Insert(PF_Container* parent,
                     const PF_InsertAttribute& d)
        : PF_Container(parent), data(d) {

	block = NULL;

    if (data.updateMode!=LX_Define::NoUpdate) {
        update();
        //calculateBorders();
    }
}


/**
 * Destructor.
 */
PF_Insert::~PF_Insert() {}


/**
 * Updates the entity buffer of this insert entity. This method
 * needs to be called whenever the block this insert is based on changes.
 */
void PF_Insert::update() {

	PF_DEBUG->print("PF_Insert::update");
        PF_DEBUG->print("PF_Insert::update: name: %s", data.name.toLatin1().data());
	PF_DEBUG->print("PF_Insert::update: insertionPoint: %f/%f", 
		data.insertionPoint.x, data.insertionPoint.y);

	if (updateEnabled==false) {
		return;
	}

    clear();

    PF_Block* blk = getBlockForInsert();
    if (blk==NULL) {
        //return NULL;
		PF_DEBUG->print("PF_Insert::update: Block is NULL");
        return;
    }

    if (isUndone()) {
		PF_DEBUG->print("PF_Insert::update: Insert is in undo list");
        return;
    }

	if (fabs(data.scaleFactor.x)<1.0e-6 || fabs(data.scaleFactor.y)<1.0e-6) {
		PF_DEBUG->print("PF_Insert::update: scale factor is 0");
		return;
	}
	
    PF_Pen tmpPen;

        /*QListIterator<PF_Entity> it = createIterator();
    PF_Entity* e;
    while ( (e = it.current()) != NULL ) {
        ++it;*/

	PF_DEBUG->print("PF_Insert::update: cols: %d, rows: %d", 
		data.cols, data.rows);
	PF_DEBUG->print("PF_Insert::update: block has %d entities", 
		blk->count());
	
    for (PF_Entity* e=blk->firstEntity(); e!=NULL; e=blk->nextEntity()) {
        for (int c=0; c<data.cols; ++c) {
            PF_DEBUG->print("PF_Insert::update: col %d", c);
            for (int r=0; r<data.rows; ++r) {
                PF_DEBUG->print("PF_Insert::update: row %d", r);

                if (e->rtti()==LX_Define::EntityInsert && 
                    data.updateMode!=LX_Define::PreviewUpdate) {

					PF_DEBUG->print("PF_Insert::update: updating sub-insert");
                    ((PF_Insert*)e)->update();
                }
				
				PF_DEBUG->print("PF_Insert::update: cloning entity");

                PF_Entity* ne = e->clone();
                ne->initId();
				ne->setUpdateEnabled(false);
                ne->setParent(this);
                ne->setVisible(getFlag(LX_Define::FlagVisible));
				
				PF_DEBUG->print("PF_Insert::update: transforming entity");

                // Move:
				PF_DEBUG->print("PF_Insert::update: move 1");
				if (fabs(data.scaleFactor.x)>1.0e-6 && 
				    fabs(data.scaleFactor.y)>1.0e-6) {
	                ne->move(data.insertionPoint +
    	                     PF_Vector(data.spacing.x/data.scaleFactor.x*c,
        	                           data.spacing.y/data.scaleFactor.y*r));
				}
				else {
	                ne->move(data.insertionPoint);
				}
                // Move because of block base point:
				PF_DEBUG->print("PF_Insert::update: move 2");
                ne->move(blk->getBasePoint()*-1);
                // Scale:
				PF_DEBUG->print("PF_Insert::update: scale");
                ne->scale(data.insertionPoint, data.scaleFactor);
                // Rotate:
				PF_DEBUG->print("PF_Insert::update: rotate");
                ne->rotate(data.insertionPoint, data.angle);
                // Select:
                ne->setSelected(isSelected());

                // individual entities can be on indiv. layers
                tmpPen = ne->getPen(false);

                // color from block (free floating):
                if (tmpPen.getColor()==PF_Color(LX_Define::FlagByBlock)) {
                    tmpPen.setColor(getPen().getColor());
                }

                // line width from block (free floating):
                if (tmpPen.getWidth()==LX_Define::WidthByBlock) {
                    tmpPen.setWidth(getPen().getWidth());
                }

                // line type from block (free floating):
                if (tmpPen.getLineType()==LX_Define::LineByBlock) {
                    tmpPen.setLineType(getPen().getLineType());
                }

                // now that we've evaluated all flags, let's strip them:
                // TODO: strip all flags (width, line type)
                //tmpPen.setColor(tmpPen.getColor().stripFlags());

                ne->setPen(tmpPen);
				
				ne->setUpdateEnabled(true);
				
				if (data.updateMode!=LX_Define::PreviewUpdate) {
					PF_DEBUG->print("PF_Insert::update: updating new entity");
					ne->update();
				}

				PF_DEBUG->print("PF_Insert::update: adding new entity");
                addEntity(ne);
            }
        }
    }
    calculateBorders();

	PF_DEBUG->print("PF_Insert::update: OK");
}



/**
 * @return Pointer to the block associated with this Insert or
 *   NULL if the block couldn't be found. Blocks are requested
 *   from the blockSource if one was supplied and otherwise from 
 *   the closest parent graphic.
 */
PF_Block* PF_Insert::getBlockForInsert() {
	if (block!=NULL) {
		return block;
	}

    PF_BlockList* blkList;

    if (data.blockSource==NULL) {
        if (getGraphic()!=NULL) {
            blkList = getGraphic()->getBlockList();
        } else {
            blkList = NULL;
        }
    } else {
        blkList = data.blockSource;
    }

    PF_Block* blk = NULL;
    if (blkList!=NULL) {
        blk = blkList->find(data.name);
    }

    if (blk!=NULL) {
    }

	block = blk;

    return blk;
}


/**
 * Is this insert visible? (re-implementation from PF_Entity)
 *
 * @return true Only if the entity and the block and the layer it is on 
 * are visible.
 * The Layer might also be NULL. In that case the layer visiblity 
 * is ignored.
 * The Block might also be NULL. In that case the block visiblity 
 * is ignored.
 */
bool PF_Insert::isVisible() {
    PF_Block* blk = getBlockForInsert();
    if (blk!=NULL) {
        if (blk->isFrozen()) {
            return false;
        }
    }

    return PF_Entity::isVisible();
}


PF_VectorSolutions PF_Insert::getRefPoints() {
	PF_VectorSolutions ret(data.insertionPoint);
	return ret;
}
    


PF_Vector PF_Insert::getNearestRef(const PF_Vector& coord,
                                     double* dist) {

	return getRefPoints().getClosest(coord, dist);
}



void PF_Insert::move(PF_Vector offset) {
	PF_DEBUG->print("PF_Insert::move: offset: %f/%f", 
		offset.x, offset.y);
	PF_DEBUG->print("PF_Insert::move1: insertionPoint: %f/%f", 
		data.insertionPoint.x, data.insertionPoint.y);
    data.insertionPoint.move(offset);
	PF_DEBUG->print("PF_Insert::move2: insertionPoint: %f/%f", 
		data.insertionPoint.x, data.insertionPoint.y);
    update();
}



void PF_Insert::rotate(PF_Vector center, double angle) {
	PF_DEBUG->print("PF_Insert::rotate1: insertionPoint: %f/%f "
	    "/ center: %f/%f", 
		data.insertionPoint.x, data.insertionPoint.y,
		center.x, center.y);
    data.insertionPoint.rotate(center, angle);
    data.angle = PF_Math::correctAngle(data.angle+angle);
	PF_DEBUG->print("PF_Insert::rotate2: insertionPoint: %f/%f", 
		data.insertionPoint.x, data.insertionPoint.y);
    update();
}



void PF_Insert::scale(PF_Vector center, PF_Vector factor) {
	PF_DEBUG->print("PF_Insert::scale1: insertionPoint: %f/%f", 
		data.insertionPoint.x, data.insertionPoint.y);
    data.insertionPoint.scale(center, factor);
    data.scaleFactor.scale(PF_Vector(0.0, 0.0), factor);
    data.spacing.scale(PF_Vector(0.0, 0.0), factor);
	PF_DEBUG->print("PF_Insert::scale2: insertionPoint: %f/%f", 
		data.insertionPoint.x, data.insertionPoint.y);
    update();
}



void PF_Insert::mirror(PF_Vector axisPoint1, PF_Vector axisPoint2) {
    data.insertionPoint.mirror(axisPoint1, axisPoint2);
	
	PF_Vector vec;
	vec.setPolar(1.0, data.angle);
	vec.mirror(PF_Vector(0.0,0.0), axisPoint2-axisPoint1);
	data.angle = vec.angle();

	data.scaleFactor.y*=-1;

    update();
}


std::ostream& operator << (std::ostream& os, const PF_Insert& i) {
    os << " Insert: " << i.getData() << std::endl;
    return os;
}

